#ifndef __CAMERA_H__
#define __CAMERA_H__

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>
#include <string.h>

struct camera {
    void *start;
    int length;
};

//开辟用户空间
struct camera buffer[4];
struct v4l2_buffer buf;

int camera_init(char *pathname);//摄像头初始化
int camera_start(int fd);//摄像头开始采集数据
int camera_dequeue(int fd, char **pic_buf, int *length);//图片出队
int camera_enqueue(int fd);//图片入队

int camera_init(char *pathname) {
    //打开摄像头
    int fd = open(pathname, O_RDWR);
    if (-1 == fd) {
        perror("open");
        return -1;
    }
    //查询摄像头支持的采集格式
    struct v4l2_fmtdesc fmt;//定义v4l2格式描述的结构体
    fmt.index = 0;
    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    char fmtBuf[64] = {0};
    while (ioctl(fd, VIDIOC_ENUM_FMT, &fmt) != -1) {
        printf("%d %s\n", fmt.index + 1, fmt.description);
        strcat(fmtBuf, fmt.description);
        fmt.index++;
    }
    //设置图片的大小和采集的格式
    struct v4l2_format req;
    req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//数据流，固定
    req.fmt.pix.width = 640;
    req.fmt.pix.height = 480;
    req.fmt.pix.field = V4L2_FIELD_ANY;//按行采集
    if (strstr(fmtBuf, "JPEG") != NULL) {
        req.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;//采集的图片的格式
    } else {
        req.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
    }
    if (ioctl(fd, VIDIOC_S_FMT, &req) == -1)//使设置的摄像头的属性的生效
    {
        perror("S_FMT");
        return -1;
    }
    return fd;
}


int camera_start(int fd) {
    //申请开辟缓存--内核空间
    struct v4l2_requestbuffers vbuf;
    vbuf.count = 4;                         //设置缓存个数
    vbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//数据流类型，固定
    vbuf.memory = V4L2_MEMORY_MMAP;         //内存为映射方式
    if (ioctl(fd, VIDIOC_REQBUFS, &vbuf) == -1) {
        perror("REQUEST");
        return -1;
    }

    //循环的读取内核空间的属性，然后与用户空间进行映射.
    int i;
    for (i = 0; i < vbuf.count; i++) {
        buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        buf.memory = V4L2_MEMORY_MMAP;
        buf.index = i;
        if (ioctl(fd, VIDIOC_QUERYBUF, &buf) == -1) {
            perror("QUERYBFU");
        }
        buffer[i].length = buf.length;
        buffer[i].start = mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset);
        if (buffer[i].start == MAP_FAILED) {
            printf("mmap");
            return -1;
        }
        //入队
        if (ioctl(fd, VIDIOC_QBUF, &buf) == -1) {
            perror("QBUF");
            return -1;
        }
    }
    enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    if (ioctl(fd, VIDIOC_STREAMON, &type) == -1) {
        perror("camera->start");
        return -1;
    }
    return 0;
}

int camera_dequeue(int fd, char **pic_buf, int *length) {
    //出队
    buf.index = 0;
    if (ioctl(fd, VIDIOC_DQBUF, &buf) == -1) {
        perror("DQBUF");
        return -1;
    }
//    printf("queue length:%ld\n",strlen(buffer[0].start));
    //图片数据处理 发送长度 和图片
    *pic_buf = buffer[buf.index].start;
    *length = buf.bytesused;
    return 0;
}

int camera_enqueue(int fd) {
    //入队
    //重新放入缓存队列
    if (ioctl(fd, VIDIOC_QBUF, &buf) == -1) {
        perror("QBUF");
        return -1;
    }
    return 0;
}

#endif
